package org.hxzon.tapestry5.components;

import java.util.List;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Events;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.RequestParameter;
import org.apache.tapestry5.corelib.base.AbstractField;
import org.apache.tapestry5.internal.util.CaptureResultCallback;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.hxzon.tapestry5.util.ValueLabel;

@Events({ EventConstants.VALIDATE, EventConstants.VALUE_CHANGED + " when 'zone' parameter is bound" })
public class SelectEx extends AbstractField {
    public static final String CHANGE_EVENT = "change";

    @Parameter(name = "value")
    private Object _value;

    @Parameter(name = "source", required = true, allowNull = false)
    private List<Object> _source;

    //when value not null,select value;
    //when select=-1,select null;else,select select
    @Parameter(name = "select", value = "0")
    private int _select;

    @Parameter(name = "valueLabel")
    private ValueLabel<Object> _valueLabel;

    @Parameter(name = "readonly", value = "false")
    private boolean _readonly;

    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL)
    private String _itemClass;

    @Inject
    private Request _request;

    @Inject
    private ComponentResources resources;

    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    private String zone;

    @Inject
    private JavaScriptSupport javascriptSupport;

    void beginRender(MarkupWriter writer) {
        writer.element("select", "name", getControlName(), "id", getClientId(), "disabled", _readonly ? "disabled" : null);

        resources.renderInformalParameters(writer);

        decorateInsideField();
        if (_select == -1) {
            writeOption(writer, (this._value == null), -1, "", _itemClass);
        }
        int size = _source.size();
        for (int i = 0; i < size; i++) {
            String label = null;
            Object curValue = _source.get(i);

            if (_valueLabel != null) {
                label = _valueLabel.toLabel(curValue, i);
            } else {
                label = curValue.toString();
            }

            boolean checked = this._value == curValue;
            if (this._value == null && _select == i) {
                checked = true;
            }
            writeOption(writer, checked, i, label, _itemClass);
        }// end for

        if (this.zone != null) {
            Link link = resources.createEventLink(CHANGE_EVENT);

            JSONObject spec = new JSONObject("selectId", getClientId(), "zoneId", zone, "url", link.toURI());

            javascriptSupport.addInitializerCall("linkSelectToZone", spec);
        }
    }

    void afterRender(MarkupWriter writer) {
        writer.end();
    }

    private void writeOption(MarkupWriter writer, boolean checked, int index, String label, String itemClass) {
        writer.element("option", "value", index, "selected", checked ? "selected" : null);
        writer.write(label);
        writer.end();
    }

    @Override
    public boolean isRequired() {
        return false;
    }

    @Override
    protected void processSubmission(String controlName) {
        String[] postedValues = _request.getParameters(controlName);
        if (postedValues == null || postedValues.length == 0) {
            _value = null;
            return;
        }
        int postedValue = Integer.valueOf(postedValues[0]);
        if (postedValue == -1) {
            _value = null;
        } else {
            _value = _source.get(postedValue);
        }

    }

    Object onChange(@RequestParameter(value = "t:selectvalue", allowBlank = true) final String selectValue) {

        CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();

        int postedValue = Integer.valueOf(selectValue);
        if (postedValue == -1) {
            _value = null;
        } else {
            _value = _source.get(postedValue);
        }
        this.resources.triggerEvent(EventConstants.VALUE_CHANGED, new Object[] { _value }, callback);

        return callback.getResult();
    }
}
